-module(trace).
-export([start/3]).

start(Position, Size, Scene) ->
    random:seed(now()),
    Cam = camera:set_render_size(Size, scene:get_camera(Scene)),
    Ray = camera:get_ray(Cam, Position),
    trace(Ray, Scene).

trace(Ray, Scene) -> trace(Ray, Scene, 0).

trace(Ray, Scene, Depth) when Depth > 100 ->
    color:black();
trace(Ray, Scene, Depth) ->
    case ray:hittest(Ray, Scene) of
        false ->
            case light:hittest(Ray, Scene) of
                false -> scene:get_background(Scene);
                Light ->
                    {Pos, _Dir} = ray:split(Ray),
                    {E, Color} = color_from_hit_light(Pos, Light),
                    color:scale(E, Color)
            end;
            
        {HitDistance, Face, _UV} ->
            Normal = face:get_normal(Face),
            Mtl = face:get_material(Face, Scene),
            Roughness = material:get_roughness(Mtl),

            {Pos, Dir} = ray:split(Ray),
            HitPos = vector:sum(Pos, vector:scale(HitDistance, Dir)),
            OutDir = get_random_dir(Normal),
            
            Weight = ray:get_weight(Ray),
            ReflectedWeight = brdf(Dir, OutDir, Normal, Roughness),
               
            ReflectedRay = ray:new(HitPos, OutDir, ReflectedWeight),
            ReflectedColor = trace(ReflectedRay, Scene, Depth+random:uniform(10)),

            
            MtlColor = material:get_color(Mtl),
            Color = color:add(MtlColor, ReflectedColor),

            {E, LColor} = color_from_lights(Pos, light:check(Pos, Scene)),
            color:scale(Weight*E, color:add(Color, LColor))
    end.


brdf(Wi, Wo, N, R) ->
    Thi = vector:ang(Wi, N),
    Tho = vector:ang(Wo, N),
    Cos = math:cos(Thi)*math:cos(Tho),
    1/(4*math:pi()*R*math:sqrt(Cos*Cos)).

get_random_dir(Normal) ->
    X = random:uniform(),
    Y = random:uniform(),
    Z = random:uniform(),
    case X*X+Y*Y+Z*Z > 1 of
        true -> get_random_dir(Normal);
        false ->
            R1 = vector:normalize(vector:new(X, Y, Z)),
            case vector:dprod(Normal, R1) < 0 of
                true -> vector:scale(-1, R1);
                false -> R1
            end
    end.


color_from_lights(_Pos, []) -> {0, color:black()};
color_from_lights(Pos, [Light|Tail]) ->
    {E, Color} = color_from_hit_light(Pos, Light),
    {E2, Color2} = color_from_lights(Pos, Tail),
    {E+E2, color:add(Color, Color2)}.
    

color_from_hit_light(Pos, Light) ->
    LPos = light:get_position(Light),
    I = light:get_intensity(Light),
    Color = light:get_color(Light),

    D = vector:distance(Pos, LPos),
    E = (I*4)/D,

    {E, Color}.
    

